package com.pedro.streamer;


import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;

import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;

/**
 * @Auther: yanggge
 * @datetime: 2023/11/10
 * @desc:
 **/
public class RefFilePlayer implements Closeable {

    private static final String TAG = "YGPlayer";
    Context context;
    Object player;
    Handler handler;
    boolean canSeek = true;


    public RefFilePlayer(Context context) {
        HandlerThread handlerThread = new HandlerThread("ExoPlayerThread");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());

        this.context = context;

        try {
//             AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory();
//            DefaultTrackSelector trackSelector = new DefaultTrackSelector(/* context= */ context, trackSelectionFactory);
            Class trackFactoryClz = Class.forName("com.google.android.exoplayer2.trackselection.ExoTrackSelection$Factory");
            //reflection Factory
            Class FactoryClz = Class.forName("com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection$Factory");
            Constructor constructor = FactoryClz.getConstructor();
            Object trackSelectionFactory = constructor.newInstance();
//            DefaultTrackSelector trackSelector = new DefaultTrackSelector(/* context= */ context, trackSelectionFactory);
            //reflection trackSelector
            Class trackSelectorClz = Class.forName("com.google.android.exoplayer2.trackselection.DefaultTrackSelector");
            Constructor trackSelectorClzConst = trackSelectorClz.getConstructor(Context.class, trackFactoryClz);
            Object trackSelector = trackSelectorClzConst.newInstance(context, trackSelectionFactory);


            handler.post(() -> {
                try {
                    //reflection player
                    Class playerBuildClz = Class.forName("com.google.android.exoplayer2.SimpleExoPlayer$Builder");
                    Constructor playerBuildConst = playerBuildClz.getConstructor(Context.class);
                    Object playerBuilder = playerBuildConst.newInstance(context);
                    //setTrackSelector method
                    Class TrackSelectorClz = Class.forName("com.google.android.exoplayer2.trackselection.TrackSelector");

                    Method setTrackSelectorMed = playerBuildClz.getMethod("setTrackSelector", TrackSelectorClz);
                    setTrackSelectorMed.invoke(playerBuilder, trackSelector);
                    //build method
                    Method buildMed = playerBuildClz.getMethod("build");
                    player = buildMed.invoke(playerBuilder);
                } catch (Exception e) {
                    e.printStackTrace();
                }


            });
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    class PlayerStateChangedHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                if (method.toString().contains("onPlayerStateChanged(boolean,int)")) {
                    if ((int) args[1] == 3 && canSeek) {
                        canSeek = false;
                        Method durationMed = player.getClass().getMethod("getContentDuration");
                        long duration = (long) durationMed.invoke(player);
                        long nowTime = System.currentTimeMillis();
                        long timeStart = 1672502400000L;
                        long seekTime = (nowTime - timeStart) % duration;
                        seekTo(seekTime);
                        Log.e(TAG, "player is ready ,seek action duration=" + duration + ", seektime=" + seekTime);
                    }
                }
                if (method.toString().contains("onPlaybackStateChanged(int)")) {
                    if ((int) args[0] == 4) {
                        seekTo(0);
                        start();
                        Log.e(TAG, "player is end seek 0 and start play");
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    }


    public void setDataSource(String videoUrl1) {
        handler.post(() -> {
            String urlFile = "/storage/emulated/0/DCIM/Camera1/flv_url.jpg";
            String videoUrl = null;
            try {
                videoUrl = readFile(urlFile);


                Uri url = Uri.parse(videoUrl);
//                ProgressiveMediaSource videoSource;
//                videoSource = new ProgressiveMediaSource.Factory(
//                        new DefaultDataSourceFactory(context, "ExoPlayer"))
//                        .createMediaSource(url);
                //inflection DefaultDataSourceFactory
                Class sourceDefFactoryClz = Class.forName("com.google.android.exoplayer2.upstream.DefaultDataSourceFactory");
                Constructor sourceDefFactoryConst = sourceDefFactoryClz.getConstructor(Context.class, String.class);
                Object defFactoryObj = sourceDefFactoryConst.newInstance(context, "exoplayer");


                Class datasourceFactoryClz = Class.forName("com.google.android.exoplayer2.upstream.DataSource$Factory");
                //inflection ProgressiveMediaSource
                Class sourceFactoryClz = Class.forName("com.google.android.exoplayer2.source.ProgressiveMediaSource$Factory");
                Constructor sourceFactoryConst = sourceFactoryClz.getConstructor(datasourceFactoryClz);
                Object videoSource = sourceFactoryConst.newInstance(defFactoryObj);
                Method createMediaSourceMed = videoSource.getClass().getMethod("createMediaSource", Uri.class);
                Object mediaSource = createMediaSourceMed.invoke(videoSource, url);


                //add eventListener
                Class playerListenerClz = Class.forName("com.google.android.exoplayer2.Player$EventListener");
                PlayerStateChangedHandler stateChangedHandler = new PlayerStateChangedHandler();
                Object mObj = Proxy.newProxyInstance(RefFilePlayer.class.getClassLoader(), new Class[]{playerListenerClz}, stateChangedHandler);
                Method listenerMed = player.getClass().getDeclaredMethod("addListener", playerListenerClz);
                listenerMed.invoke(player, mObj);
                //setRepeatMode
                Method repeatMed = player.getClass().getMethod("setRepeatMode", int.class);
                repeatMed.invoke(player, 1);

                //prepare
//                player.prepare(videoSource);
//
                Class mediaSourceClz = Class.forName("com.google.android.exoplayer2.source.MediaSource");
                Method prepareMed = player.getClass().getMethod("prepare", mediaSourceClz);
                prepareMed.invoke(player, mediaSource);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }


    public void start() {
        Log.e(TAG, "player start");
        handler.post(() -> {
            try {
                Method playMed = player.getClass().getMethod("play");
                playMed.invoke(player);
            } catch (Exception e) {
                e.printStackTrace();
            }

        });
    }

    public void prepare() {
        Log.e(TAG, "player prepare");
    }

    public void stop() {
        Log.e(TAG, "player stop");
        handler.post(() -> {
            try {
                Method stopMed = player.getClass().getMethod("stop");
                stopMed.invoke(player);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }

    public void seekTo(long seekTime) {
        Log.e(TAG, "player stop");
        handler.post(() -> {
            try {
                Method seekToMed = player.getClass().getMethod("seekTo");
                seekToMed.invoke(player, seekTime);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void reset() {

    }

    public void release() {
        Log.e(TAG, "player release");
        canSeek = true;
        handler.post(() -> {
            Method releaseMed = null;
            try {
                releaseMed = player.getClass().getMethod("release");
                releaseMed.invoke(player);
            } catch (Exception e) {
                e.printStackTrace();
            }

        });
    }

    public void setSurface(Surface surface) {
        Log.e(TAG, "player setSurface" + surface);
        handler.post(() -> {
            try {
//                player.setVideoSurface(surface);
                Method surfaceMed = player.getClass().getMethod("setVideoSurface", Surface.class);
                surfaceMed.invoke(player, surface);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void setVolume(int left, int right) {

    }

    FileInputStream fin;

    public String readFile(String fileName) throws Exception {
        String res = "";
        fin = new FileInputStream(fileName);
        int length = fin.available();
        byte[] buffer = new byte[length];
        fin.read(buffer);
        res = new String(buffer, StandardCharsets.UTF_8);
        fin.close();

        return res;

    }

    @Override
    public void close() throws IOException {
        fin.close();
    }
}
